import direct.directbase.DirectStart                    # Starts Panda
from pandac.PandaModules import *                       # Basic Panda modules
from direct.showbase.DirectObject import DirectObject   # For event handling
from direct.actor.Actor import Actor                    # For animated models
from direct.interval.IntervalGlobal import *            # For compound intervals
from direct.task import Task                            # For update functions
from direct.particles.ParticleEffect import ParticleEffect  #For particles
from direct.gui.OnscreenImage import OnscreenImage
import sys, math, random, os

from fire import Fire
from room import Room
from player import Player
from door import Door

class World(DirectObject):
    def __init__(self):
        #messenger.toggleVerbose()
    
        #Initialization
        base.disableMouse()
        base.enableParticles()
        base.fire_dict = {}
        base.door_dict = {}
        camera.setPosHpr(0,0,0,0,0,0)
        self.loadModels()
        self.setupLights()
        
        #title screen
        self.titlescreen = OnscreenImage("Art/art/Fireman Fred Title Screen.png")
        self.titlescreen.reparentTo(render2d)
        self.at_title = True
        self.game_over = False
        
        #Initialize collisions
        self.setupCollisions()
        
        #Load rooms
        self.loadRooms()
        self.loadDoors()
        
        #Fire Information
        fireslit = 0
        while(fireslit < 10):
            room = int(random.uniform(0,len(self.roomList)))
            if len(self.roomList[room].fireList) < 2:
                self.roomList[room].spawnFireRandom()
                print self.roomList[room].name
                fireslit += 1
        
        #Keymap
        self.keyMap = {"left":False, "right":False, "forward":False}
        
        #Tasks
        taskMgr.add(self.updateFires, "fireTask")
        taskMgr.add(self.updateLights, "lightsTask")
        taskMgr.add(self.updateDoors, "doorsTask")
        taskMgr.add(self.gameOverCheck, "gameoverTask")
        taskMgr.add(self.winCheck, "winTask")
        
        #Window Properties
        props = WindowProperties()
        props.setCursorHidden(True)
        props.setMouseMode(1)
        base.win.requestProperties(props)
        
        
        #Initialize a player
        self.player = Player()
        self.player.setupCollisions(base.cTrav)
        
        
        #Control Information
        self.accept("escape", sys.exit)
        self.accept("enter-up", self.changeScreen)
        self.accept("w", self.player.setKey, ["forward", 1])
        self.accept("s", self.player.setKey, ["backward", 1])
        self.accept("d", self.player.setKey, ["right", 1])
        self.accept("a", self.player.setKey, ["left", 1])
        self.accept("w-up", self.player.setKey, ["forward", 0])
        self.accept("d-up", self.player.setKey, ["right", 0])
        self.accept("a-up", self.player.setKey, ["left", 0])
        self.accept("s-up", self.player.setKey, ["backward", 0])
        self.accept('mouse1', self.player.doAction)
        self.accept('mouse1-up', self.player.endAction)
        self.accept('e-up', self.player.switchMode)
        self.accept('p', self.debugPrintPlayerPosition)
        
        #Collision handler setups
        self.accept("target-fireSphere", self.sprayFire)
        self.accept("target-again-fireSphere", self.sprayFire)
        self.accept("target-doorTube", self.hitDoor)
        self.accept("target-again-doorTube", self.hitDoor)
        
        #Timing for door effects
        self.lastTime = 0
    
    def changeScreen(self):
        if(self.at_title):
            self.at_title = False
            self.titlescreen.destroy()
        if(self.game_over):
            self.game_over = False
            self.gameoverscreen.destroy()
            ppython = sys.executable
            os.execl(ppython, ppython, * sys.argv)
    
    def win(self):
        self.game_over = True
        self.gameoverscreen = OnscreenImage("Art/art/Fireman Fred Win Screen.png")
        self.gameoverscreen.reparentTo(render2d)
        
    def gameOver(self):
        self.game_over = True
        self.gameoverscreen = OnscreenImage("Art/art/Fireman Fred Game Over Screen.png")
        self.gameoverscreen.reparentTo(render2d)
        
    
    def loadModels(self):
        self.env = loader.loadModel("Art/models/Structure")
        self.env.reparentTo(render)
        self.stairCollision = loader.loadModel("Art/models/Stair Collision No Clip")
        self.stairCollision.reparentTo(render)
        self.visibleFloor = loader.loadModel("Art/models/Visible floor")
        self.visibleFloor.reparentTo(render)
        self.stairModels = loader.loadModel("Art/models/Stairs All")
        self.stairModels.reparentTo(render)
        self.targets = []
        
    def loadRooms(self):
        self.roomList = open("lvl/rooms.dat").readlines()
        self.roomList = [line.rstrip() for line in self.roomList]
        self.roomList = [Room("lvl/" + filename) for filename in self.roomList]
        
        """"
        #Debugging for room coordinates
        for r in self.roomList:
            for i, rects in enumerate(r.roomRects):
                testmodel = loader.loadModel("Art/models/Player")
                testmodel2 = loader.loadModel("Art/models/Player")
                testmodel.reparentTo(render)
                testmodel2.reparentTo(render)
                testmodel.setPos(r.roomRects[i][0],r.roomRects[i][1],r.zLevel)
                testmodel2.setPos(r.roomRects[i][2],r.roomRects[i][3],r.zLevel)
        """
        
    def loadDoors(self):
        self.doorList = open("lvl/doors.dat").readlines()
        self.doorList = [line.split('|') for line in self.doorList]
 
        #make sure door coords are getting loaded right
        # for a_door in self.doorList:
            # print a_door[2].split(',')
            
        self.doorList = [Door(x = float(door_data[3].split(',')[0]),
                              y = float(door_data[3].split(',')[1]),
                              z = float(door_data[3].split(',')[2]), 
                              orient = int(door_data[4]),
                              id = int(door_data[0])) for door_data in self.doorList]
        
        for a_door in self.doorList:
            base.door_dict[a_door.id] = a_door
                
    def setupLights(self):
        """loads initial lighting"""
        #Light 1
        self.dirLight = DirectionalLight("dirLight")
        self.dirLight.setColor((.25,.15,.05,1))
        self.dirLightNP = render.attachNewNode(self.dirLight)
        self.dirLightNP.setHpr(230,-25,0)
        render.setLight(self.dirLightNP)
        
        #Light 2
        self.dirLight2 = DirectionalLight("dirLight2")
        self.dirLight2.setColor((.25,.25,.05,1))
        self.dirLight2NP = render.attachNewNode(self.dirLight2)
        self.dirLight2NP.setHpr(50,25,0)
        render.setLight(self.dirLight2NP)
        
        self.lastFlicker = 0
        self.flicker = 0
        self.flickerChance = 0
        
        #Ambient Light
        self.ambientLight = AmbientLight("ambientLight")
        self.ambientLight.setColor((.105,.09,.07,1))
        self.ambientLightNP = render.attachNewNode(self.ambientLight)
        render.setLight(self.ambientLightNP)
               
    def setupCollisions(self):
        # Initialize the collision traverser.
        base.cTrav = CollisionTraverser()
         
        # Initialize the Pusher collision handler.
        base.pusher = CollisionHandlerPusher()
        base.cHandler = CollisionHandlerEvent()
        base.cHandler.setInPattern("target-%in")
        base.cHandler.addAgainPattern('target-again-%in')
        
        #base.cTrav.showCollisions(render)

    def updateLights(self, task):
        if(self.at_title):
            return Task.cont
    
        msTime = task.time * 1000
        if msTime > self.lastFlicker + 150 - self.flickerChance * 250:
            self.flicker = random.uniform(1.5,2.7)
            self.flicker *= 6
            self.flicker /= 18
            self.dirLight.setColor((self.flicker, self.flicker / 2, self.flicker / 5, 1))
            self.dirLight2.setColor((self.flicker, self.flicker / 2, self.flicker / 5, 1))
            
            self.lastFlicker = msTime
            self.flickerChance = math.fabs(self.flicker - .5)
            self.flickerChance *= self.flickerChance
            self.flickerChance *= 2
        
        return Task.cont
           
    def updateFires(self, task):

        if(self.at_title):
            return Task.cont

        #Manages Spreading
        for i, r in enumerate(self.roomList):
            r.updateFires(task);   
            if task.time - r.lastSpread > 10 and (float(r.curHealth)/r.maxHealth) < .5 and len(r.fireList)/r.fireList[0] > .5:
                self.roomList[i].lastSpread = task.time
                roll = random.uniform(1,100)
                if roll > 80:
                    lit = False
                    while(not lit):
                        room = int(random.uniform(0,len(self.roomList)))
                        if len(self.roomList[room].fireList) < 2 and self.roomList[room].isDestroyed == False:
                            self.roomList[room].spawnFireRandom()
                            print self.roomList[room].name
                            lit = True
                    

        return Task.cont

    def debugPrintPlayerPosition(self):
        print "Position:"
        print "X: " + str(self.player.position[0])
        print "Y: " + str(self.player.position[1]) + "\n"
            

    def sprayFire(self, cEntry):
        '''do damage to fire'''
        if(self.player.extinguisher_mode and self.player.spraying):
            base.fire_dict[cEntry.getIntoNodePath()].health -= 10
            if(base.fire_dict[cEntry.getIntoNodePath()].isDone()):
                cEntry.getIntoNodePath().getParent().remove()
                
    def hitDoor(self, cEntry):
        '''do damage to door'''
        if(self.player.melee_mode and self.player.acting and self.player.can_strike):
            self.player.can_strike = False
            self.player.has_struck = True
            #print "axed door!"
            
            base.door_dict[cEntry.getIntoNodePath()].damage()
            if(base.door_dict[cEntry.getIntoNodePath()].destroyed):
                cEntry.getIntoNodePath().getParent().remove()
                del base.door_dict[cEntry.getIntoNodePath()]
                
    def gameOverCheck(self, task):
        if Room.roomsCollapsed >= 5:
            self.gameOver()
            
        return Task.cont
    
    def winCheck(self, task):
        fires = 0
        for r in self.roomList:
            fires += len(r.fireList) - 1
            
        if fires == 0:
            self.win()
            
        return Task.cont    
        
    def updateDoors(self, task):
        elapsed = task.time - self.lastTime
        for key in base.door_dict:
            base.door_dict[key].particleTime += elapsed
            if base.door_dict[key].particleTime > .5:
                base.door_dict[key].collapseParticle.disable() 
            
        self.lastTime = task.time
        
        return Task.cont
w = World()
run()